-- ****
-- T80(b) core. In an effort to merge and maintain bug fixes ....
--
--
-- Ver 303 add undocumented DDCB and FDCB opcodes by TobiFlex 20.04.2010
-- Ver 302 fixed IO cycle timing, tested thanks to Alessandro.
-- Ver 301 parity flag is just parity for 8080, also overflow for Z80, by Sean Riddle
-- Ver 300 started tidyup. Rmoved some auto_wait bits from 0247 which caused problems
--
-- MikeJ March 2005
-- Latest version from www.fpgaarcade.com (original www.opencores.org)
--
-- ****
--
-- Z80 compatible microprocessor core
--
-- Version : 0247
--
-- Copyright (c) 2001-2002 Daniel Wallner (jesus@opencores.org)
--
-- All rights reserved
--
-- Redistribution and use in source and synthezised forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- Redistributions in synthesized form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- Neither the name of the author nor the names of other contributors may
-- be used to endorse or promote products derived from this software without
-- specific prior written permission.
--
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--
-- Please report bugs to the author, but before you do so, please
-- make sure that this is not a derivative work and that
-- you have the latest version of this file.
--
-- The latest version of this file can be found at:
--      http://www.opencores.org/cvsweb.shtml/t80/
--
-- Limitations :
--
-- File history :
--
--      0208 : First complete release
--
--      0210 : Fixed wait and halt
--
--      0211 : Fixed Refresh addition and IM 1
--
--      0214 : Fixed mostly flags, only the block instructions now fail the zex regression test
--
--      0232 : Removed refresh address output for Mode > 1 and added DJNZ M1_n fix by Mike Johnson
--
--      0235 : Added clock enable and IM 2 fix by Mike Johnson
--
--      0237 : Changed 8080 I/O address output, added IntE output
--
--      0238 : Fixed (IX/IY+d) timing and 16 bit ADC and SBC zero flag
--
--      0240 : Added interrupt ack fix by Mike Johnson, changed (IX/IY+d) timing and changed flags in GB mode
--
--      0242 : Added I/O wait, fixed refresh address, moved some registers to RAM
--
--      0247 : Fixed bus req/ack cycle
--

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use work.T80_Pack.all;

entity T80 is
  generic(
    Mode   : integer := 0;  -- 0 => Z80, 1 => Fast Z80, 2 => 8080, 3 => GB
    IOWait : integer := 0;  -- 1 => Single cycle I/O, 1 => Std I/O cycle
    Flag_C : integer := 0;
    Flag_N : integer := 1;
    Flag_P : integer := 2;
    Flag_X : integer := 3;
    Flag_H : integer := 4;
    Flag_Y : integer := 5;
    Flag_Z : integer := 6;
    Flag_S : integer := 7
    );
  port(
    RESET_n    : in  std_logic;
    CLK_n      : in  std_logic;
    CEN        : in  std_logic;
    WAIT_n     : in  std_logic;
    INT_n      : in  std_logic;
    NMI_n      : in  std_logic;
    BUSRQ_n    : in  std_logic;
    M1_n       : out std_logic;
    IORQ       : out std_logic;
    NoRead     : out std_logic;
    Write      : out std_logic;
    RFSH_n     : out std_logic;
    HALT_n     : out std_logic;
    BUSAK_n    : out std_logic;
    A          : out std_logic_vector(15 downto 0);
    DInst      : in  std_logic_vector(7 downto 0);
    DI         : in  std_logic_vector(7 downto 0);
    DO         : out std_logic_vector(7 downto 0);
    MC         : out std_logic_vector(2 downto 0);
    TS         : out std_logic_vector(2 downto 0);
    IntCycle_n : out std_logic;
    IntE       : out std_logic;
    Stop       : out std_logic
    );
end T80;

architecture rtl of T80 is

  constant aNone              : std_logic_vector(2 downto 0) := "111";
  constant aBC                : std_logic_vector(2 downto 0) := "000";
  constant aDE                : std_logic_vector(2 downto 0) := "001";
  constant aXY                : std_logic_vector(2 downto 0) := "010";
  constant aIOA               : std_logic_vector(2 downto 0) := "100";
  constant aSP                : std_logic_vector(2 downto 0) := "101";
  constant aZI                : std_logic_vector(2 downto 0) := "110";

  -- Registers
  signal ACC, F               : std_logic_vector(7 downto 0);
  signal Ap, Fp               : std_logic_vector(7 downto 0);
  signal I                    : std_logic_vector(7 downto 0);
  signal R                    : unsigned(7 downto 0);
  signal SP, PC               : unsigned(15 downto 0);

  signal RegDIH               : std_logic_vector(7 downto 0);
  signal RegDIL               : std_logic_vector(7 downto 0);
  signal RegBusA              : std_logic_vector(15 downto 0);
  signal RegBusB              : std_logic_vector(15 downto 0);
  signal RegBusC              : std_logic_vector(15 downto 0);
  signal RegAddrA_r           : std_logic_vector(2 downto 0);
  signal RegAddrA             : std_logic_vector(2 downto 0);
  signal RegAddrB_r           : std_logic_vector(2 downto 0);
  signal RegAddrB             : std_logic_vector(2 downto 0);
  signal RegAddrC             : std_logic_vector(2 downto 0);
  signal RegWEH               : std_logic;
  signal RegWEL               : std_logic;
  signal Alternate            : std_logic;

  -- Help Registers
  signal TmpAddr              : std_logic_vector(15 downto 0);        -- Temporary address register
  signal IR                   : std_logic_vector(7 downto 0);         -- Instruction register
  signal ISet                 : std_logic_vector(1 downto 0);         -- Instruction set selector
  signal RegBusA_r            : std_logic_vector(15 downto 0);

  signal ID16                 : signed(15 downto 0);
  signal Save_Mux             : std_logic_vector(7 downto 0);

  signal TState               : unsigned(2 downto 0);
  signal MCycle               : std_logic_vector(2 downto 0);
  signal IntE_FF1             : std_logic;
  signal IntE_FF2             : std_logic;
  signal Halt_FF              : std_logic;
  signal BusReq_s             : std_logic;
  signal BusAck               : std_logic;
  signal ClkEn                : std_logic;
  signal NMI_s                : std_logic;
  signal INT_s                : std_logic;
  signal IStatus              : std_logic_vector(1 downto 0);

  signal DI_Reg               : std_logic_vector(7 downto 0);
  signal T_Res                : std_logic;
  signal XY_State             : std_logic_vector(1 downto 0);
  signal Pre_XY_F_M           : std_logic_vector(2 downto 0);
  signal NextIs_XY_Fetch      : std_logic;
  signal XY_Ind               : std_logic;
  signal No_BTR               : std_logic;
  signal BTR_r                : std_logic;
  signal Auto_Wait            : std_logic;
  signal Auto_Wait_t1         : std_logic;
  signal Auto_Wait_t2         : std_logic;
  signal IncDecZ              : std_logic;

  -- ALU signals
  signal BusB                 : std_logic_vector(7 downto 0);
  signal BusA                 : std_logic_vector(7 downto 0);
  signal ALU_Q                : std_logic_vector(7 downto 0);
  signal F_Out                : std_logic_vector(7 downto 0);

  -- Registered micro code outputs
  signal Read_To_Reg_r        : std_logic_vector(4 downto 0);
  signal Arith16_r            : std_logic;
  signal Z16_r                : std_logic;
  signal ALU_Op_r             : std_logic_vector(3 downto 0);
  signal Save_ALU_r           : std_logic;
  signal PreserveC_r          : std_logic;
  signal MCycles              : std_logic_vector(2 downto 0);

  -- Micro code outputs
  signal MCycles_d            : std_logic_vector(2 downto 0);
  signal TStates              : std_logic_vector(2 downto 0);
  signal IntCycle             : std_logic;
  signal NMICycle             : std_logic;
  signal Inc_PC               : std_logic;
  signal Inc_WZ               : std_logic;
  signal IncDec_16            : std_logic_vector(3 downto 0);
  signal Prefix               : std_logic_vector(1 downto 0);
  signal Read_To_Acc          : std_logic;
  signal Read_To_Reg          : std_logic;
  signal Set_BusB_To          : std_logic_vector(3 downto 0);
  signal Set_BusA_To          : std_logic_vector(3 downto 0);
  signal ALU_Op               : std_logic_vector(3 downto 0);
  signal Save_ALU             : std_logic;
  signal PreserveC            : std_logic;
  signal Arith16              : std_logic;
  signal Set_Addr_To          : std_logic_vector(2 downto 0);
  signal Jump                 : std_logic;
  signal JumpE                : std_logic;
  signal JumpXY               : std_logic;
  signal Call                 : std_logic;
  signal RstP                 : std_logic;
  signal LDZ                  : std_logic;
  signal LDW                  : std_logic;
  signal LDSPHL               : std_logic;
  signal IORQ_i               : std_logic;
  signal Special_LD           : std_logic_vector(2 downto 0);
  signal ExchangeDH           : std_logic;
  signal ExchangeRp           : std_logic;
  signal ExchangeAF           : std_logic;
  signal ExchangeRS           : std_logic;
  signal I_DJNZ               : std_logic;
  signal I_CPL                : std_logic;
  signal I_CCF                : std_logic;
  signal I_SCF                : std_logic;
  signal I_RETN               : std_logic;
  signal I_BT                 : std_logic;
  signal I_BC                 : std_logic;
  signal I_BTR                : std_logic;
  signal I_RLD                : std_logic;
  signal I_RRD                : std_logic;
  signal I_INRC               : std_logic;
  signal SetDI                : std_logic;
	signal SetEI                : std_logic;
	signal IMode                : std_logic_vector(1 downto 0);
	signal Halt                 : std_logic;
	signal XYbit_undoc          : std_logic;


begin

  mcode : T80_MCode
    generic map(
      Mode   => Mode,
      Flag_C => Flag_C,
      Flag_N => Flag_N,
      Flag_P => Flag_P,
      Flag_X => Flag_X,
      Flag_H => Flag_H,
      Flag_Y => Flag_Y,
      Flag_Z => Flag_Z,
      Flag_S => Flag_S)
    port map(
      IR          => IR,
      ISet        => ISet,
      MCycle      => MCycle,
      F           => F,
      NMICycle    => NMICycle,
      IntCycle    => IntCycle,
      XY_State    => XY_State,
      MCycles     => MCycles_d,
      TStates     => TStates,
      Prefix      => Prefix,
      Inc_PC      => Inc_PC,
      Inc_WZ      => Inc_WZ,
      IncDec_16   => IncDec_16,
      Read_To_Acc => Read_To_Acc,
      Read_To_Reg => Read_To_Reg,
      Set_BusB_To => Set_BusB_To,
      Set_BusA_To => Set_BusA_To,
      ALU_Op      => ALU_Op,
      Save_ALU    => Save_ALU,
      PreserveC   => PreserveC,
      Arith16     => Arith16,
      Set_Addr_To => Set_Addr_To,
      IORQ        => IORQ_i,
      Jump        => Jump,
      JumpE       => JumpE,
      JumpXY      => JumpXY,
      Call        => Call,
      RstP        => RstP,
      LDZ         => LDZ,
      LDW         => LDW,
      LDSPHL      => LDSPHL,
      Special_LD  => Special_LD,
      ExchangeDH  => ExchangeDH,
      ExchangeRp  => ExchangeRp,
      ExchangeAF  => ExchangeAF,
      ExchangeRS  => ExchangeRS,
      I_DJNZ      => I_DJNZ,
      I_CPL       => I_CPL,
      I_CCF       => I_CCF,
      I_SCF       => I_SCF,
      I_RETN      => I_RETN,
      I_BT        => I_BT,
      I_BC        => I_BC,
      I_BTR       => I_BTR,
      I_RLD       => I_RLD,
      I_RRD       => I_RRD,
      I_INRC      => I_INRC,
      SetDI       => SetDI,
      SetEI       => SetEI,
      IMode       => IMode,
      Halt        => Halt,
      NoRead      => NoRead,
      Write       => Write,
      XYbit_undoc => XYbit_undoc);

  alu : T80_ALU
    generic map(
      Mode   => Mode,
      Flag_C => Flag_C,
      Flag_N => Flag_N,
      Flag_P => Flag_P,
      Flag_X => Flag_X,
      Flag_H => Flag_H,
      Flag_Y => Flag_Y,
      Flag_Z => Flag_Z,
      Flag_S => Flag_S)
    port map(
      Arith16 => Arith16_r,
      Z16     => Z16_r,
      ALU_Op  => ALU_Op_r,
      IR      => IR(5 downto 0),
      ISet    => ISet,
      BusA    => BusA,
      BusB    => BusB,
      F_In    => F,
      Q       => ALU_Q,
      F_Out   => F_Out);

  ClkEn <= CEN and not BusAck;

  T_Res <= '1' when TState = unsigned(TStates) else '0';

  NextIs_XY_Fetch <= '1' when XY_State /= "00" and XY_Ind = '0' and
                              ((Set_Addr_To = aXY) or
                               (MCycle = "001" and IR = "11001011") or
                               (MCycle = "001" and IR = "00110110")) else '0';

  Save_Mux <= BusB when ExchangeRp = '1' else
              DI_Reg when Save_ALU_r = '0' else
              ALU_Q;

  process (RESET_n, CLK_n)
  begin
    if RESET_n = '0' then
      PC <= (others => '0');  -- Program Counter
      A <= (others => '0');
      TmpAddr <= (others => '0');
      IR <= "00000000";
      ISet <= "00";
      XY_State <= "00";
      IStatus <= "00";
      MCycles <= "000";
      DO <= "00000000";

      ACC <= (others => '1');
      F <= (others => '1');
      Ap <= (others => '1');
      Fp <= (others => '1');
      I <= (others => '0');
      R <= (others => '0');
      SP <= (others => '1');
      Alternate <= '0';

      Read_To_Reg_r <= "00000";
      F <= (others => '1');
      Arith16_r <= '0';
      BTR_r <= '0';
      Z16_r <= '0';
      ALU_Op_r <= "0000";
      Save_ALU_r <= '0';
      PreserveC_r <= '0';
      XY_Ind <= '0';

    elsif CLK_n'event and CLK_n = '1' then

      if ClkEn = '1' then

        ALU_Op_r <= "0000";
        Save_ALU_r <= '0';
        Read_To_Reg_r <= "00000";

        MCycles <= MCycles_d;

        if IMode /= "11" then
          IStatus <= IMode;
        end if;

        Arith16_r <= Arith16;
        PreserveC_r <= PreserveC;
        if ISet = "10" and ALU_OP(2) = '0' and ALU_OP(0) = '1' and MCycle = "011" then
          Z16_r <= '1';
        else
          Z16_r <= '0';
        end if;

        if MCycle  = "001" and TState(2) = '0' then
          -- MCycle = 1 and TState = 1, 2, or 3

          if TState = 2 and Wait_n = '1' then
            if Mode < 2 then
              A(7 downto 0) <= std_logic_vector(R);
              A(15 downto 8) <= I;
              R(6 downto 0) <= R(6 downto 0) + 1;
            end if;

            if Jump = '0' and Call = '0' and NMICycle = '0' and IntCycle = '0' and not (Halt_FF = '1' or Halt = '1') then
              PC <= PC + 1;
            end if;

            if IntCycle = '1' and IStatus = "01" then
              IR <= "11111111";
            elsif Halt_FF = '1' or (IntCycle = '1' and IStatus = "10") or NMICycle = '1' then
              IR <= "00000000";
            else
              IR <= DInst;
            end if;

            ISet <= "00";
            if Prefix /= "00" then
              if Prefix = "11" then
                if IR(5) = '1' then
                  XY_State <= "10";
                else
                  XY_State <= "01";
                end if;
              else
                if Prefix = "10" then
                  XY_State <= "00";
                  XY_Ind <= '0';
                end if;
                ISet <= Prefix;
              end if;
            else
              XY_State <= "00";
              XY_Ind <= '0';
            end if;
          end if;

        else
          -- either (MCycle > 1) OR (MCycle = 1 AND TState > 3)

          if MCycle = "110" then
            XY_Ind <= '1';
            if Prefix = "01" then
              ISet <= "01";
            end if;
          end if;

          if T_Res = '1' then
            BTR_r <= (I_BT or I_BC or I_BTR) and not No_BTR;
            if Jump = '1' then
              A(15 downto 8) <= DI_Reg;
              A(7 downto 0) <= TmpAddr(7 downto 0);
              PC(15 downto 8) <= unsigned(DI_Reg);
              PC(7 downto 0) <= unsigned(TmpAddr(7 downto 0));
            elsif JumpXY = '1' then
              A <= RegBusC;
              PC <= unsigned(RegBusC);
            elsif Call = '1' or RstP = '1' then
              A <= TmpAddr;
              PC <= unsigned(TmpAddr);
            elsif MCycle = MCycles and NMICycle = '1' then
              A <= "0000000001100110";
              PC <= "0000000001100110";
            elsif MCycle = "011" and IntCycle = '1' and IStatus = "10" then
              A(15 downto 8) <= I;
              A(7 downto 0) <= TmpAddr(7 downto 0);
              PC(15 downto 8) <= unsigned(I);
              PC(7 downto 0) <= unsigned(TmpAddr(7 downto 0));
            else
              case Set_Addr_To is
                when aXY =>
                  if XY_State = "00" then
                    A <= RegBusC;
                  else
                    if NextIs_XY_Fetch = '1' then
                      A <= std_logic_vector(PC);
                    else
                      A <= TmpAddr;
                    end if;
                  end if;
                when aIOA =>
                  if Mode = 3 then
                    -- Memory map I/O on GBZ80
                    A(15 downto 8) <= (others => '1');
                  elsif Mode = 2 then
                    -- Duplicate I/O address on 8080
                    A(15 downto 8) <= DI_Reg;
                  else
                    A(15 downto 8) <= ACC;
                  end if;
                  A(7 downto 0) <= DI_Reg;
                when aSP =>
                  A <= std_logic_vector(SP);
                when aBC =>
                  if Mode = 3 and IORQ_i = '1' then
                    -- Memory map I/O on GBZ80
                    A(15 downto 8) <= (others => '1');
                    A(7 downto 0) <= RegBusC(7 downto 0);
                  else
                    A <= RegBusC;
                  end if;
                when aDE =>
                  A <= RegBusC;
                when aZI =>
                  if Inc_WZ = '1' then
                    A <= std_logic_vector(unsigned(TmpAddr) + 1);
                  else
                    A(15 downto 8) <= DI_Reg;
                    A(7 downto 0) <= TmpAddr(7 downto 0);
                  end if;
                when others =>
                  A <= std_logic_vector(PC);
              end case;
            end if;

            Save_ALU_r <= Save_ALU;
            ALU_Op_r <= ALU_Op;

            if I_CPL = '1' then
              -- CPL
              ACC <= not ACC;
              F(Flag_Y) <= not ACC(5);
              F(Flag_H) <= '1';
              F(Flag_X) <= not ACC(3);
              F(Flag_N) <= '1';
            end if;
            if I_CCF = '1' then
              -- CCF
              F(Flag_C) <= not F(Flag_C);
              F(Flag_Y) <= ACC(5);
              F(Flag_H) <= F(Flag_C);
              F(Flag_X) <= ACC(3);
              F(Flag_N) <= '0';
            end if;
            if I_SCF = '1' then
              -- SCF
              F(Flag_C) <= '1';
              F(Flag_Y) <= ACC(5);
              F(Flag_H) <= '0';
              F(Flag_X) <= ACC(3);
              F(Flag_N) <= '0';
            end if;
          end if;

          if TState = 2 and Wait_n = '1' then
            if ISet = "01" and MCycle = "111" then
              IR <= DInst;
            end if;
            if JumpE = '1' then
              PC <= unsigned(signed(PC) + signed(DI_Reg));
            elsif Inc_PC = '1' then
              PC <= PC + 1;
            end if;
            if BTR_r = '1' then
              PC <= PC - 2;
            end if;
            if RstP = '1' then
              TmpAddr <= (others =>'0');
              TmpAddr(5 downto 3) <= IR(5 downto 3);
            end if;
          end if;
          if TState = 3 and MCycle = "110" then
            TmpAddr <= std_logic_vector(signed(RegBusC) + signed(DI_Reg));
          end if;

          if (TState = 2 and Wait_n = '1') or (TState = 4 and MCycle = "001") then
            if IncDec_16(2 downto 0) = "111" then
              if IncDec_16(3) = '1' then
                SP <= SP - 1;
              else
                SP <= SP + 1;
              end if;
            end if;
          end if;

          if LDSPHL = '1' then
            SP <= unsigned(RegBusC);
          end if;
          if ExchangeAF = '1' then
            Ap <= ACC;
            ACC <= Ap;
            Fp <= F;
            F <= Fp;
          end if;
          if ExchangeRS = '1' then
            Alternate <= not Alternate;
          end if;
        end if;

        if TState = 3 then
          if LDZ = '1' then
            TmpAddr(7 downto 0) <= DI_Reg;
          end if;
          if LDW = '1' then
            TmpAddr(15 downto 8) <= DI_Reg;
          end if;

          if Special_LD(2) = '1' then
            case Special_LD(1 downto 0) is
              when "00" =>
                ACC <= I;
                F(Flag_P) <= IntE_FF2;
              when "01" =>
                ACC <= std_logic_vector(R);
                F(Flag_P) <= IntE_FF2;
              when "10" =>
                I <= ACC;
              when others =>
                R <= unsigned(ACC);
            end case;
          end if;
        end if;

        if (I_DJNZ = '0' and Save_ALU_r = '1') or ALU_Op_r = "1001" then
          if Mode = 3 then
            F(6) <= F_Out(6);
            F(5) <= F_Out(5);
            F(7) <= F_Out(7);
            if PreserveC_r = '0' then
              F(4) <= F_Out(4);
            end if;
          else
            F(7 downto 1) <= F_Out(7 downto 1);
            if PreserveC_r = '0' then
              F(Flag_C) <= F_Out(0);
            end if;
          end if;
        end if;
        if T_Res = '1' and I_INRC = '1' then
          F(Flag_H) <= '0';
          F(Flag_N) <= '0';
          if DI_Reg(7 downto 0) = "00000000" then
            F(Flag_Z) <= '1';
          else
            F(Flag_Z) <= '0';
          end if;
          F(Flag_S) <= DI_Reg(7);
          F(Flag_P) <= not (DI_Reg(0) xor DI_Reg(1) xor DI_Reg(2) xor DI_Reg(3) xor
                            DI_Reg(4) xor DI_Reg(5) xor DI_Reg(6) xor DI_Reg(7));
        end if;

        if TState = 1 then
          DO <= BusB;
          if I_RLD = '1' then
            DO(3 downto 0) <= BusA(3 downto 0);
            DO(7 downto 4) <= BusB(3 downto 0);
          end if;
          if I_RRD = '1' then
            DO(3 downto 0) <= BusB(7 downto 4);
            DO(7 downto 4) <= BusA(3 downto 0);
          end if;
        end if;

        if T_Res = '1' then
          Read_To_Reg_r(3 downto 0) <= Set_BusA_To;
          Read_To_Reg_r(4) <= Read_To_Reg;
          if Read_To_Acc = '1' then
            Read_To_Reg_r(3 downto 0) <= "0111";
            Read_To_Reg_r(4) <= '1';
          end if;
        end if;

        if TState = 1 and I_BT = '1' then
          F(Flag_X) <= ALU_Q(3);
          F(Flag_Y) <= ALU_Q(1);
          F(Flag_H) <= '0';
          F(Flag_N) <= '0';
        end if;
        if I_BC = '1' or I_BT = '1' then
          F(Flag_P) <= IncDecZ;
        end if;

        if (TState = 1 and Save_ALU_r = '0') or
          (Save_ALU_r = '1' and ALU_OP_r /= "0111") then
          case Read_To_Reg_r is
            when "10111" =>
              ACC <= Save_Mux;
            when "10110" =>
              DO <= Save_Mux;
            when "11000" =>
              SP(7 downto 0) <= unsigned(Save_Mux);
            when "11001" =>
              SP(15 downto 8) <= unsigned(Save_Mux);
            when "11011" =>
              F <= Save_Mux;
            when others =>
          end case;
          if XYbit_undoc='1' then
            DO <= ALU_Q;
          end if;
        end if;

      end if;

    end if;

  end process;

---------------------------------------------------------------------------
--
-- BC('), DE('), HL('), IX and IY
--
---------------------------------------------------------------------------
  process (CLK_n)
  begin
    if CLK_n'event and CLK_n = '1' then
      if ClkEn = '1' then
        -- Bus A / Write
        RegAddrA_r <= Alternate & Set_BusA_To(2 downto 1);
        if XY_Ind = '0' and XY_State /= "00" and Set_BusA_To(2 downto 1) = "10" then
          RegAddrA_r <= XY_State(1) & "11";
        end if;

        -- Bus B
        RegAddrB_r <= Alternate & Set_BusB_To(2 downto 1);
        if XY_Ind = '0' and XY_State /= "00" and Set_BusB_To(2 downto 1) = "10" then
          RegAddrB_r <= XY_State(1) & "11";
        end if;

        -- Address from register
        RegAddrC <= Alternate & Set_Addr_To(1 downto 0);
        -- Jump (HL), LD SP,HL
        if (JumpXY = '1' or LDSPHL = '1') then
          RegAddrC <= Alternate & "10";
        end if;
        if ((JumpXY = '1' or LDSPHL = '1') and XY_State /= "00") or (MCycle = "110") then
          RegAddrC <= XY_State(1) & "11";
        end if;

        if I_DJNZ = '1' and Save_ALU_r = '1' and Mode < 2 then
          IncDecZ <= F_Out(Flag_Z);
        end if;
        if (TState = 2 or (TState = 3 and MCycle = "001")) and IncDec_16(2 downto 0) = "100" then
          if ID16 = 0 then
            IncDecZ <= '0';
          else
            IncDecZ <= '1';
          end if;
        end if;

        RegBusA_r <= RegBusA;
      end if;
    end if;
  end process;

  RegAddrA <=
    -- 16 bit increment/decrement
    Alternate & IncDec_16(1 downto 0) when (TState = 2 or
                                            (TState = 3 and MCycle = "001" and IncDec_16(2) = '1')) and XY_State = "00" else
    XY_State(1) & "11" when (TState = 2 or
                             (TState = 3 and MCycle = "001" and IncDec_16(2) = '1')) and IncDec_16(1 downto 0) = "10" else
    -- EX HL,DL
    Alternate & "10" when ExchangeDH = '1' and TState = 3 else
    Alternate & "01" when ExchangeDH = '1' and TState = 4 else
    -- Bus A / Write
    RegAddrA_r;

  RegAddrB <=
    -- EX HL,DL
    Alternate & "01" when ExchangeDH = '1' and TState = 3 else
    -- Bus B
    RegAddrB_r;

  ID16 <= signed(RegBusA) - 1 when IncDec_16(3) = '1' else
          signed(RegBusA) + 1;

  process (Save_ALU_r, Auto_Wait_t1, ALU_OP_r, Read_To_Reg_r,
           ExchangeDH, IncDec_16, MCycle, TState, Wait_n)
  begin
    RegWEH <= '0';
    RegWEL <= '0';
    if (TState = 1 and Save_ALU_r = '0') or
      (Save_ALU_r = '1' and ALU_OP_r /= "0111") then
      case Read_To_Reg_r is
        when "10000" | "10001" | "10010" | "10011" | "10100" | "10101" =>
          RegWEH <= not Read_To_Reg_r(0);
          RegWEL <= Read_To_Reg_r(0);
        when others =>
      end case;
    end if;

    if ExchangeDH = '1' and (TState = 3 or TState = 4) then
      RegWEH <= '1';
      RegWEL <= '1';
    end if;

    if IncDec_16(2) = '1' and ((TState = 2 and Wait_n = '1' and MCycle /= "001") or (TState = 3 and MCycle = "001")) then
      case IncDec_16(1 downto 0) is
        when "00" | "01" | "10" =>
          RegWEH <= '1';
          RegWEL <= '1';
        when others =>
      end case;
    end if;
  end process;

  process (Save_Mux, RegBusB, RegBusA_r, ID16,
           ExchangeDH, IncDec_16, MCycle, TState, Wait_n)
  begin
    RegDIH <= Save_Mux;
    RegDIL <= Save_Mux;

    if ExchangeDH = '1' and TState = 3 then
      RegDIH <= RegBusB(15 downto 8);
      RegDIL <= RegBusB(7 downto 0);
    end if;
    if ExchangeDH = '1' and TState = 4 then
      RegDIH <= RegBusA_r(15 downto 8);
      RegDIL <= RegBusA_r(7 downto 0);
    end if;

    if IncDec_16(2) = '1' and ((TState = 2 and MCycle /= "001") or (TState = 3 and MCycle = "001")) then
      RegDIH <= std_logic_vector(ID16(15 downto 8));
      RegDIL <= std_logic_vector(ID16(7 downto 0));
    end if;
  end process;

  Regs : T80_Reg
    port map(
      Clk   => CLK_n,
      CEN   => ClkEn,
      WEH   => RegWEH,
      WEL   => RegWEL,
      AddrA => RegAddrA,
      AddrB => RegAddrB,
      AddrC => RegAddrC,
      DIH   => RegDIH,
      DIL   => RegDIL,
      DOAH  => RegBusA(15 downto 8),
      DOAL  => RegBusA(7 downto 0),
      DOBH  => RegBusB(15 downto 8),
      DOBL  => RegBusB(7 downto 0),
      DOCH  => RegBusC(15 downto 8),
      DOCL  => RegBusC(7 downto 0));

---------------------------------------------------------------------------
--
-- Buses
--
---------------------------------------------------------------------------
  process (CLK_n)
  begin
    if CLK_n'event and CLK_n = '1' then
      if ClkEn = '1' then
        case Set_BusB_To is
          when "0111" =>
            BusB <= ACC;
          when "0000" | "0001" | "0010" | "0011" | "0100" | "0101" =>
            if Set_BusB_To(0) = '1' then
              BusB <= RegBusB(7 downto 0);
            else
              BusB <= RegBusB(15 downto 8);
            end if;
          when "0110" =>
            BusB <= DI_Reg;
          when "1000" =>
            BusB <= std_logic_vector(SP(7 downto 0));
          when "1001" =>
            BusB <= std_logic_vector(SP(15 downto 8));
          when "1010" =>
            BusB <= "00000001";
          when "1011" =>
            BusB <= F;
          when "1100" =>
            BusB <= std_logic_vector(PC(7 downto 0));
          when "1101" =>
            BusB <= std_logic_vector(PC(15 downto 8));
          when "1110" =>
            BusB <= "00000000";
          when others =>
            BusB <= "--------";
        end case;

        case Set_BusA_To is
          when "0111" =>
            BusA <= ACC;
          when "0000" | "0001" | "0010" | "0011" | "0100" | "0101" =>
            if Set_BusA_To(0) = '1' then
              BusA <= RegBusA(7 downto 0);
            else
              BusA <= RegBusA(15 downto 8);
            end if;
          when "0110" =>
            BusA <= DI_Reg;
          when "1000" =>
            BusA <= std_logic_vector(SP(7 downto 0));
          when "1001" =>
            BusA <= std_logic_vector(SP(15 downto 8));
          when "1010" =>
            BusA <= "00000000";
          when others =>
            BusB <= "--------";
        end case;
        if XYbit_undoc='1' then
          BusA <= DI_Reg;
          BusB <= DI_Reg;
        end if;
      end if;
    end if;
  end process;

---------------------------------------------------------------------------
--
-- Generate external control signals
--
---------------------------------------------------------------------------
  process (RESET_n,CLK_n)
  begin
    if RESET_n = '0' then
      RFSH_n <= '1';
    elsif CLK_n'event and CLK_n = '1' then
      if CEN = '1' then
        if MCycle = "001" and ((TState = 2  and Wait_n = '1') or TState = 3) then
          RFSH_n <= '0';
        else
          RFSH_n <= '1';
        end if;
      end if;
    end if;
  end process;

  MC <= std_logic_vector(MCycle);
  TS <= std_logic_vector(TState);
  DI_Reg <= DI;
  HALT_n <= not Halt_FF;
  BUSAK_n <= not BusAck;
  IntCycle_n <= not IntCycle;
  IntE <= IntE_FF1;
  IORQ <= IORQ_i;
  Stop <= I_DJNZ;

-------------------------------------------------------------------------
--
-- Syncronise inputs
--
-------------------------------------------------------------------------
  process (RESET_n, CLK_n)
    variable OldNMI_n : std_logic;
  begin
    if RESET_n = '0' then
      BusReq_s <= '0';
      INT_s <= '0';
      NMI_s <= '0';
      OldNMI_n := '0';
    elsif CLK_n'event and CLK_n = '1' then
      if CEN = '1' then
        BusReq_s <= not BUSRQ_n;
        INT_s <= not INT_n;
        if NMICycle = '1' then
          NMI_s <= '0';
        elsif NMI_n = '0' and OldNMI_n = '1' then
          NMI_s <= '1';
        end if;
        OldNMI_n := NMI_n;
      end if;
    end if;
  end process;

-------------------------------------------------------------------------
--
-- Main state machine
--
-------------------------------------------------------------------------
  process (RESET_n, CLK_n)
  begin
    if RESET_n = '0' then
      MCycle <= "001";
      TState <= "000";
      Pre_XY_F_M <= "000";
      Halt_FF <= '0';
      BusAck <= '0';
      NMICycle <= '0';
      IntCycle <= '0';
      IntE_FF1 <= '0';
      IntE_FF2 <= '0';
      No_BTR <= '0';
      Auto_Wait_t1 <= '0';
      Auto_Wait_t2 <= '0';
      M1_n <= '1';
    elsif CLK_n'event and CLK_n = '1' then
      if CEN = '1' then
        Auto_Wait_t1 <= Auto_Wait;
        Auto_Wait_t2 <= Auto_Wait_t1;
        No_BTR <= (I_BT and (not IR(4) or not F(Flag_P))) or
                  (I_BC and (not IR(4) or F(Flag_Z) or not F(Flag_P))) or
                  (I_BTR and (not IR(4) or F(Flag_Z)));
        if TState = 2 then
          if SetEI = '1' then
            IntE_FF1 <= '1';
            IntE_FF2 <= '1';
          end if;
          if I_RETN = '1' then
            IntE_FF1 <= IntE_FF2;
          end if;
        end if;
        if TState = 3 then
          if SetDI = '1' then
            IntE_FF1 <= '0';
            IntE_FF2 <= '0';
          end if;
        end if;
        if IntCycle = '1' or NMICycle = '1' then
          Halt_FF <= '0';
        end if;
        if MCycle = "001" and TState = 2 and Wait_n = '1' then
          M1_n <= '1';
        end if;
        if BusReq_s = '1' and BusAck = '1' then
        else
          BusAck <= '0';
          if TState = 2 and Wait_n = '0' then
          elsif T_Res = '1' then
            if Halt = '1' then
              Halt_FF <= '1';
            end if;
            if BusReq_s = '1' then
              BusAck <= '1';
            else
              TState <= "001";
              if NextIs_XY_Fetch = '1' then
                MCycle <= "110";
                Pre_XY_F_M <= MCycle;
                if IR = "00110110" and Mode = 0 then
                  Pre_XY_F_M <= "010";
                end if;
              elsif (MCycle = "111") or
                (MCycle = "110" and Mode = 1 and ISet /= "01") then
                MCycle <= std_logic_vector(unsigned(Pre_XY_F_M) + 1);
              elsif (MCycle = MCycles) or
                No_BTR = '1' or
                (MCycle = "010" and I_DJNZ = '1' and IncDecZ = '1') then
                M1_n <= '0';
                MCycle <= "001";
                IntCycle <= '0';
                NMICycle <= '0';
                if NMI_s = '1' and Prefix = "00" then
                  NMICycle <= '1';
                  IntE_FF1 <= '0';
                elsif (IntE_FF1 = '1' and INT_s = '1') and Prefix = "00" and SetEI = '0' then
                  IntCycle <= '1';
                  IntE_FF1 <= '0';
                  IntE_FF2 <= '0';
                end if;
              else
                MCycle <= std_logic_vector(unsigned(MCycle) + 1);
              end if;
            end if;
          else
            if Auto_Wait = '1' nand Auto_Wait_t2 = '0' then

              TState <= TState + 1;
            end if;
          end if;
        end if;
        if TState = 0 then
          M1_n <= '0';
        end if;
      end if;
    end if;
  end process;

  process (IntCycle, NMICycle, MCycle)
  begin
    Auto_Wait <= '0';
    if IntCycle = '1' or NMICycle = '1' then
      if MCycle = "001" then
        Auto_Wait <= '1';
      end if;
    end if;
  end process;

end;
